package com.sojson.util.init;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.io.Resource;

import com.sojson.base.impl.BaseUtilDto;
import com.sojson.constant.Constant;
import com.sojson.constant.ConstantByMap;
import com.sojson.constant.IpConfig;
import com.sojson.util.ExpUtil;
import com.sojson.util.encrypt.bothway.alike.jasypt.JasyptUtil;
import com.sojson.util.file.FileUtil;
import com.sojson.util.init.interfaces.IRunStart;
import com.sojson.util.log.LogUtil;
import com.sojson.util.properties.PropertiesUtil;
import com.sojson.util.properties.YmlUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 应用程序初始化工具类
 *
 * @author liu
 * @date 2021-05-18
 */
@Slf4j
public class InitUtil {

    /** 配置文件的位置 */
    public static final String URL_BASE = "env/";
    /** 默认配置文件的位置 */
    private static final String URL_DEFAULT = URL_BASE + "default/**";
    /** 加密的前缀 */
    public static final String EN_PREFIX = "ENC(";
    /** 计划任务 */
    private static List<IRunStart> runStarts = new ArrayList<>(10);
    /** 运行结束信息 */
    private static List<String> runEndMsgs = new ArrayList<>(10);

    /**
     * 开始运行
     *
     * @param env
     */
    public static void run(ConfigurableEnvironment env) {
        log.info("开始执行应用程序自定义初始化操作！");
        try {
            // 设置日志ID
            LogUtil.setTraceId("main");

            // 设置自定义属性
            setEnv(env);

            // 创建项目缓存目录结构
            createFileStructure();

            for (IRunStart run : runStarts) {
                run.run();
            }
        } catch (IOException e) {
            ExpUtil.throwEx(e);
        }
        log.info("应用程序自定义初始化操作执行完毕！");
    }

    /**
     * 运行结束
     *
     * @throws Exception
     */
    public static void runEnd() throws Exception {
        Runtime currRuntime = Runtime.getRuntime();
        int nFreeMemory = (int)(currRuntime.freeMemory() / 1024 / 1024);
        int nTotalMemory = (int)(currRuntime.totalMemory() / 1024 / 1024);
        log.info("JVM状态:");
        log.info("JVM总内存(" + nTotalMemory + "/M)");
        log.info("JVM空闲内存(" + nFreeMemory + "/M)");

        if (runEndMsgs.size() > 0) {
            for (String msg : runEndMsgs) {
                log.info(msg);
            }
        }
    }

    /**
     * 运行结束
     *
     * @throws Exception
     */
    public static void runEndWeb() throws Exception {
        runEnd();

        log.info("当前接口的全路径为: " + IpConfig.getUrl());
        log.info("当前接口的公网IP为: " + IpConfig.getUrl(IpConfig.getIp()));
        log.info("当前接口的局域网IP为: " + IpConfig.getUrl(IpConfig.getHostAddress()));
    }

    public static void setEnv(ConfigurableEnvironment env) throws IOException {
        String active = env.getProperty("spring.profiles.active");
        log.info("开始加载" + active + "环境下自定义属性！");

        Map<String, Object> hashMap = new HashMap<>();
        List<Resource> resources = PropertiesUtil.getResources(ConstantByMap.CLASSPATHX_ + URL_DEFAULT);
        resources.addAll(PropertiesUtil.getResources(
            ConstantByMap.CLASSPATHX_ + URL_BASE + active + "/**"));
        for (Resource res : resources) {
            if (res.getFilename().endsWith(".properties")) {
                Properties properties = PropertiesUtil.getProperties(res);
                Set<Object> keySet = properties.keySet();
                for (Object key : keySet) {
                    Object object = properties.get(key);
                    String val = null;
                    if (object != null && (val = object.toString()).startsWith(EN_PREFIX) && JasyptUtil.isEn) {
                        val = JasyptUtil.decrypt(val.substring(4, val.length() - 1));
                    }
                    hashMap.put(key.toString(), val);
                }
            } else if (res.getFilename().endsWith(".yml")) {
                Properties properties = YmlUtil.getProperties(res);
                Set<Object> keySet = properties.keySet();
                for (Object key : keySet) {
                    Object object = properties.get(key);
                    String val = null;
                    if (object != null && (val = object.toString()).startsWith(EN_PREFIX) && JasyptUtil.isEn) {
                        val = JasyptUtil.decrypt(val.substring(4, val.length() - 1));
                    }
                    hashMap.put(key.toString(), val);
                }
            } else {
                continue;
            }
        }

        MutablePropertySources propertySources = env.getPropertySources();
        propertySources.addLast(new OriginTrackedMapPropertySource("MY_PROPERTIES", hashMap));
        Constant.PROPERTIES = env;
        log.info("加载" + active + "环境下自定义属性完毕！");
    }

    /**
     * 创建项目缓存目录结构
     */
    private static void createFileStructure() {
        // 开始创建目录
        log.info("正在创建目录结构！");
        FileUtil.createFileStructure();
        // 目录创建完成
        log.info("目录结构创建完成！");
    }

    public static class FileCopy extends BaseUtilDto {

        /***/
        private static final long serialVersionUID = -3434892693094099760L;
        /** 是否是文件(true:文件,false:目录) */
        private boolean isFile;
        /** 文件名 */
        private String fileName;
        /** 源目录 */
        private String resource;
        /** 目标目录 */
        private String target;

        /** 获取是否是文件(true:文件,false:目录) */
        public boolean getIsFile() {
            return isFile;
        }

        /** 设置是否是文件(true:文件,false:目录) */
        public void setIsFile(boolean isFile) {
            this.isFile = isFile;
        }

        /** 获取文件名 */
        public String getFileName() {
            return fileName;
        }

        /** 设置文件名 */
        public void setFileName(String fileName) {
            if (fileName.startsWith("/") || fileName.startsWith("\\")) {
                if (fileName.startsWith("/")) {
                    fileName = fileName.substring(1);
                }
                if (fileName.startsWith("\\")) {
                    fileName = fileName.substring(2);
                }
            }
            if (fileName.endsWith("/") || fileName.endsWith("\\")) {
                setIsFile(false);
                if (fileName.endsWith("/")) {
                    fileName = fileName.substring(0, fileName.length() - 1);
                }
                if (fileName.endsWith("\\")) {
                    fileName = fileName.substring(0, fileName.length() - 2);
                }
            } else {
                setIsFile(true);
            }
            this.fileName = fileName;
        }

        /** 获取源目录 */
        public String getResource() {
            return resource;
        }

        /** 设置源目录 */
        public void setResource(String resource) {
            setFileName(resource);
            this.resource = resource;
        }

        /** 设置源目录(开头为classpath:) */
        public void setResourceClasspath(String resource) {
            setFileName(resource);
            this.resource = ConstantByMap.CLASSPATHX_ + resource;
        }

        /** 获取目标目录 */
        public String getTarget() {
            return target;
        }

        /** 设置目标目录 */
        public void setTarget(String target) {
            this.target = target;
        }

        /**
         * 设置源目录
         */
        public static FileCopy resource(String resource) {
            FileCopy fileCopy = new FileCopy();
            fileCopy.setResource(resource);
            return fileCopy;
        }

        /**
         * 设置源目录
         */
        public static FileCopy resourceClasspath(String resource) {
            FileCopy fileCopy = new FileCopy();
            fileCopy.setResourceClasspath(resource);
            return fileCopy;
        }

        /**
         * 设置目标目录
         */
        public FileCopy target(String target) {
            setTarget(target);
            return this;
        }

    }

    /**
     * 添加运行任务
     */
    public static void add(IRunStart run) {
        InitUtil.runStarts.add(run);
    }

    /**
     * 添加运行结束信息
     */
    public static void add(String msg) {
        InitUtil.runEndMsgs.add(msg);
    }

}